Day 14 我們整理了第一份 Agent 評測報告。
到目前為止,平台已經可以做到:
evals/cases.json
-> batch runner
-> SimpleAgent
-> trace logging
-> evaluator
-> eval_run_*.json
這表示 Trace 和 Eval 的基本流程已經跑通。
但 Day 14 也看到了目前最大的限制:
我們還在使用
FakeLLMClient。
FakeLLMClient 很適合在前期建立平台,因為它不需要 API key、不需要網路,也不會有額外成本。
不過它畢竟不是真正的 LLM。
所以從今天開始,我們要把 Agent 從 fake client 接到 real LLM。
本篇會使用 Gemini API,並以 Gemini Flash 作為主要模型。
今天要做到的是:
在不大改
SimpleAgent的前提下,新增一個可以呼叫 Gemini API 的 LLM client。
會完成:
GEMINI_API_KEY。agents/gemini_llm.py。agents/client_factory.py,讓系統可以切換 fake / Gemini。app.py,讓手動執行可以選擇 LLM provider。evals/runner.py,讓 batch evaluation 可以跑 Gemini baseline。今天先不做:
今天的重點很單純:
先把真正的 LLM 接進既有平台,讓後面的評測結果更接近真實 Agent 行為。
一開始不直接接 LLM,是刻意的設計。
如果 Day 2 就直接串 Gemini API,讀者可能會同時遇到很多問題:
這會讓文章焦點變得很散。
所以前兩週先用 FakeLLMClient 建立穩定的工程骨架:
Agent Runner
Trace
SQLite
Trace Viewer
Eval Dataset
Batch Runner
Evaluator
Baseline Report
等平台骨架完成後,再接真正的 LLM。
這樣一來,我們可以確認今天的改動只集中在 LLM client 替換,而不是整個系統一起重寫。
今天會新增兩個檔案,並修改兩個檔案。
agent-testing-platform/
app.py
agents/
__init__.py
fake_llm.py
gemini_llm.py
client_factory.py
simple_agent.py
evals/
__init__.py
cases.json
runner.py
evaluators.py
今天新增:
| 檔案 | 用途 |
|---|---|
agents/gemini_llm.py |
呼叫 Gemini API 的 LLM client |
agents/client_factory.py |
根據環境變數建立 fake 或 Gemini client |
今天修改:
| 檔案 | 修改內容 |
|---|---|
app.py |
改用 create_llm_client() 建立 LLM client |
evals/runner.py |
讓 batch evaluation 可以切換 LLM provider |
Gemini API 的官方 Python SDK 是 google-genai。
在 agent-testing-platform/ 專案根目錄安裝:
python3 -m pip install google-genai
如果你有使用 virtual environment,請先啟動環境再安裝。
例如:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install google-genai
安裝完成後,可以用以下指令確認:
python3 -m pip show google-genai
Gemini API 需要 API key。
這類 key 不應該寫死在程式碼裡,也不應該 commit 到 Git repository。
今天先用環境變數設定。
在終端機執行:
export GEMINI_API_KEY="你的 Gemini API key"
接著設定模型名稱:
export GEMINI_MODEL="gemini-3.6-flash"
這裡使用 Flash 系列,是因為它通常適合做教學專案的第一個 real LLM baseline:
模型名稱會隨官方更新而改變。
如果執行時遇到 model not found,請到 Gemini API 的 Models 文件確認你帳號目前可用的 Flash 模型,並調整 GEMINI_MODEL。
前面我們已經讓 SimpleAgent 依賴一個抽象的 LLM client。
也就是說,SimpleAgent 不需要知道背後是:
它只需要知道這個 client 有 chat(messages) 方法。
今天就利用這個設計新增 Gemini 版本。
新增 agents/gemini_llm.py:
import os
from google import genai
class GeminiLLMClient:
def __init__(self, model: str | None = None):
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
raise ValueError("GEMINI_API_KEY is not set")
self.client = genai.Client(api_key=api_key)
self.model = model or os.getenv("GEMINI_MODEL", "gemini-3.6-flash")
def chat(self, messages: list[dict]) -> dict:
prompt = self._messages_to_prompt(messages)
interaction = self.client.interactions.create(
model=self.model,
input=prompt,
)
return {
"type": "final_answer",
"content": interaction.output_text or "",
}
def _messages_to_prompt(self, messages: list[dict]) -> str:
parts = []
for message in messages:
role = message["role"].upper()
content = message["content"]
parts.append(f"{role}:\n{content}")
return "\n\n".join(parts)
這個檔案的重點有三個。
第一,從環境變數讀取 GEMINI_API_KEY。
如果沒有設定,就直接丟出錯誤:
raise ValueError("GEMINI_API_KEY is not set")
這樣比讓程式在 SDK 內部失敗更清楚。
第二,從環境變數讀取 GEMINI_MODEL。
如果沒有設定,就使用預設值:
gemini-3.6-flash
第三,chat() 回傳格式仍然維持我們自己的 Agent 內部格式:
{
"type": "final_answer",
"content": interaction.output_text or "",
}
這點很重要。
因為 SimpleAgent 目前已經認得這種格式:
if response["type"] == "final_answer":
return AgentResult(answer=response["content"])
所以新增 Gemini client 時,不需要大改 SimpleAgent。
你可能會注意到,GeminiLLMClient 目前只回傳:
"type": "final_answer"
它不會回傳:
"type": "tool_call"
也就是說,今天接上 Gemini 後,計算題可能會變成由 Gemini 直接回答,而不是呼叫我們 Day 3 實作的 calculator tool。
這是刻意的取捨。
今天如果同時做 Gemini tool calling,會多出不少新問題:
這些都值得做,但不是 Day 15 的主要目標。
今天先完成最小整合:
SimpleAgent -> GeminiLLMClient -> Gemini Interactions API -> final answer
Tool calling 可以放到後面的延伸功能。
接下來要讓系統可以選擇使用 fake 或 Gemini。
如果我們直接在 app.py 和 evals/runner.py 到處寫:
GeminiLLMClient()
以後要切換 provider 會很麻煩。
所以今天新增一個小型 factory。
新增 agents/client_factory.py:
import os
from agents.fake_llm import FakeLLMClient
from agents.gemini_llm import GeminiLLMClient
def create_llm_client():
provider = os.getenv("LLM_PROVIDER", "fake").lower()
if provider == "fake":
return FakeLLMClient()
if provider == "gemini":
return GeminiLLMClient()
raise ValueError(f"Unknown LLM provider: {provider}")
這個檔案負責根據 LLM_PROVIDER 建立對應 client。
目前支援兩種:
LLM_PROVIDER |
使用的 client |
|---|---|
fake |
FakeLLMClient |
gemini |
GeminiLLMClient |
如果沒有設定 LLM_PROVIDER,預設仍然使用:
fake
這樣可以保留前兩週的行為。
也就是說,原本的執行方式仍然有效:
python3 app.py
python3 -m evals.runner
只有想使用 Gemini 時,才需要額外指定:
LLM_PROVIDER=gemini python3 -m evals.runner
接著讓手動測試也可以使用 factory。
修改 app.py:
from agents.client_factory import create_llm_client
from agents.simple_agent import SimpleAgent
def main():
user_task = input("Task: ")
agent = SimpleAgent(llm_client=create_llm_client())
result = agent.run(user_task)
print("Answer:", result.answer)
if result.tool_calls:
print("Tool calls:")
for tool_call in result.tool_calls:
print(f"- tool_name: {tool_call.tool_name}")
print(f" tool_input: {tool_call.tool_input}")
print(f" tool_output: {tool_call.tool_output}")
if __name__ == "__main__":
main()
這段修改的重點是:
agent = SimpleAgent(llm_client=create_llm_client())
以前是直接寫死:
agent = SimpleAgent(llm_client=FakeLLMClient())
現在則交給 create_llm_client() 決定。
手動測 fake client:
python3 app.py
手動測 Gemini:
LLM_PROVIDER=gemini python3 app.py
接著讓 batch evaluation 也支援 Gemini。
目前 evals/runner.py 裡可能有這兩行:
from agents.fake_llm import FakeLLMClient
from agents.simple_agent import SimpleAgent
修改 evals/runner.py,把 FakeLLMClient import 換成 create_llm_client:
from agents.client_factory import create_llm_client
from agents.simple_agent import SimpleAgent
接著找到建立 Agent 的地方。
原本可能是:
agent = SimpleAgent(llm_client=FakeLLMClient())
修改 evals/runner.py:
agent = SimpleAgent(llm_client=create_llm_client())
完整概念會變成:
def run_evaluation() -> dict[str, Any]:
init_db()
agent = SimpleAgent(llm_client=create_llm_client())
cases = load_cases()
run_id = create_run_id()
results = []
for test_case in cases:
...
這樣 runner 不需要知道目前用的是 fake 還是 Gemini。
它只負責:
讀取 cases -> 呼叫 agent -> 評分 -> 輸出結果
LLM provider 的選擇交給 agents/client_factory.py。
先確認前兩週的 fake baseline 仍然可以跑。
在 agent-testing-platform/ 專案根目錄執行:
python3 -m evals.runner
因為沒有設定 LLM_PROVIDER,所以預設會使用:
fake
你應該會得到和 Day 14 類似的結果。
例如:
Total cases: 15
Passed: 5
Failed: 10
Success rate: 33.3%
實際數字會依照你目前程式碼和 test cases 稍微不同。
重點是:原本流程不應該因為今天新增 Gemini 而壞掉。
接著執行 Gemini 版本。
確認你已經設定:
export GEMINI_API_KEY="你的 Gemini API key"
export GEMINI_MODEL="gemini-3.6-flash"
然後執行:
LLM_PROVIDER=gemini python3 -m evals.runner
這次 runner 會走:
evals.runner
-> create_llm_client()
-> GeminiLLMClient
-> Gemini API
-> SimpleAgent
-> evaluator
執行完成後,同樣會在 data/eval_runs/ 產生結果檔。
例如:
data/eval_runs/eval_run_20260906_153000.json
可以用以下指令檢查:
python3 -m json.tool data/eval_runs/eval_run_20260906_153000.json
檔名請換成你實際產生的檔名。
接上 Gemini 後,結果通常會比 fake client 更接近真實任務。
例如原本 fake client 可能會回答:
Fake response for: 請回答 HTTP 狀態碼 404 通常代表什麼
Gemini 比較可能回答:
HTTP 狀態碼 404 通常代表找不到請求的資源。
這樣 contains evaluator 就能判定通過,因為答案包含:
找不到
不過,不是每一題都一定會通過。
例如 exact_match 題目:
{
"input": "請只回覆 OK",
"expected": "OK",
"grading_method": "exact_match"
}
模型可能回覆:
OK
也可能回覆:
OK。
甚至可能回覆:
好的,OK。
對人來說這些都很接近,但對 exact_match evaluator 來說,只有完全等於 OK 才會通過。
這就是 Agent 評測會遇到的真實問題:
模型能力變強後,測試不一定全部通過,因為格式遵循、評分方式與任務定義仍然會影響結果。
今天不是只為了把成功率提高。
更重要的是,我們現在有兩種 baseline。
第一種是 fake baseline:
FakeLLMClient + eval dataset
它的用途是確認平台流程穩定。
例如:
第二種是 Gemini baseline:
GeminiLLMClient + eval dataset
它的用途是觀察真實 LLM 的表現。
例如:
這兩種 baseline 都有價值。
fake baseline 幫助我們測平台,Gemini baseline 幫助我們測 Agent 行為。
GEMINI_API_KEY is not set如果看到:
ValueError: GEMINI_API_KEY is not set
代表目前終端機沒有設定 API key。
請重新執行:
export GEMINI_API_KEY="你的 Gemini API key"
注意:這個設定只會存在目前的終端機 session。
如果你關掉終端機後重開,需要重新設定。
No module named 'google'如果看到:
ModuleNotFoundError: No module named 'google'
通常代表還沒安裝 SDK,或是安裝在不同 Python 環境。
請確認:
python3 -m pip show google-genai
如果沒有結果,重新安裝:
python3 -m pip install google-genai
model not found如果看到 model not found,通常代表 GEMINI_MODEL 指定的模型名稱不可用。
請改用你帳號目前可用的 Flash 模型。
例如:
export GEMINI_MODEL="你的可用 Flash 模型名稱"
模型名稱要以 Gemini API 官方 Models 頁面為準。
今天我們把 Agent 從 fake client 接到真正的 Gemini API。
完成的內容包含:
google-genai。GEMINI_API_KEY 管理 API key。agents/gemini_llm.py。agents/client_factory.py。app.py,讓手動測試可以切換 provider。evals/runner.py,讓 batch evaluation 可以跑 Gemini baseline。FakeLLMClient 作為平台流程測試用 baseline。今天完成後,系統多了一個重要能力:
同一套 Agent 測試平台,可以測 fake client,也可以測真正的 LLM。
從 Day 15 開始,我們的 evaluation result 會更接近真實 Agent 開發會遇到的問題。
接上 Gemini 後,我們會開始看到更真實的失敗案例。
有些失敗可能是:
Day 16 會正式進入 Failure Analysis。
下一篇會定義 Agent 常見失敗類型,並把 failure_type 加進 evaluation result。
這樣平台就不只會告訴我們:
這一題 failed。
而是能進一步告訴我們:
這一題是 format_error。
這一題是 wrong_answer。
這一題是 instruction_error。
這會讓後續的 dashboard、prompt A/B testing 和 retry 策略更有依據。